package cn.tedu.net.reflection;
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

//本类用来测试反射
public class Test1_Reflect {
    /*单元测试方法:是Java测试的最小单位,使用灵活
    * 语法要求:被@Test注解修饰 + 没有参数 + 是public的 + 没有返回值类型即void
    * 使用时需要导包:
    * Add JUnit4 library to the bulid path:import org.junit.Test;
    * */
    //通过单元测试方法获取类的字节码对象(类对象)
    @Test
    public void getClazz() throws Exception{
        //右键选择要复制的类名,选择copy/Past Special 选择第一个
        //本方法的参数是类的全路径名-->包名+类名,并且需要抛出异常
        Class<?> student1 = Class.forName("cn.tedu.net.reflection.Student");
        Class<?> student2 = Student.class;
        //先创建匿名对象,再获取字节码对象
        Class<? extends Student> student3 = new Student().getClass();
        System.out.println(student1);  //反射得到字节码Class对象
        System.out.println(student2.getName()); //获取全路径名(包名+类名)
        System.out.println(student3.getSimpleName());// 获取类名
        System.out.println(student1.getPackage().getName());//获取包名
    }
    //通过单元测试来测试如何获取构造方法
    @Test
    public void getConstructor(){
        //1.获取字节码Class对象 (类对象)
        Class<Student> clazz = Student.class;
        //获取构造方法们,并放入cs数组中
        Constructor<?>[] cs = clazz.getConstructors();
        //获取每个构造方法
        //使用增强for循环完成
        for (Constructor<?> c : cs) {
            //操作每轮循环到的当前构造函数
            System.out.println(c.getName());//获取当前构造的名字
            Class<?>[] cps = c.getParameterTypes();//获取构造函数中的参数类型
            System.out.println(Arrays.toString(cps));
        }

    }
    //通过单元测试来获取成员方法
    @Test
    public void getFunction(){
        Class<? extends Student> clazz = new Student().getClass();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());//获取方法名
            System.out.println("*********");
            Class<?>[] parameterTypes = method.getParameterTypes();//获取方法的参数类型
            System.out.println(Arrays.toString(parameterTypes));
        }
    }
    //通过单元测试来获取成员变量
    @Test
    public void getFields() throws Exception{
        Class<?> clazz = Class.forName("cn.tedu.net.reflection.Student");
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
            System.out.println(field.getName());//获取成员变量名 注意成员变量的修饰符必须是public才能获取到
            System.out.println("***********");
            System.out.println(field.getType().getName());//获得变量类型

        }
    }
    //通过单元测试反射创建对象
    /*总结:利用反射技术创建对象的两种方式:
    * 1.通过字节码对象直接调用new Lnstance(),触发的是无参构造来创建对象
    * 2.根据方法参数先获取指定的构造方法对象,再通过此对象调用newLnstance()
    * 触发的是对应参数的构造函数来创建对象,注意参数类型是字节码对象*/
    @Test
    public void createObject() throws Exception{
        //获取类对象
        Class<Student> clazz = Student.class;
        //创建对象
        Student obj = clazz.newInstance();//触发的是无参构造
        System.out.println(obj);
        //获取指定的构造方法,根据方法的参数来指定,传入的不是参数的直接类型而是字节码对象
        Constructor<Student> cs = clazz.getConstructor(String.class, int.class);
        //利用刚刚拿到的构造方法对象创建Student对象,触发的是全参构造
        Student 小猫 = cs.newInstance("小猫", 5);
        System.out.println(小猫);

        //测试子类方法
        System.out.println(小猫.age);
        System.out.println(小猫.name);
        小猫.tell();
    }

}
